#include "Banksystem.hh"
#include "Utils.hh"
#include <iostream>
#include <fstream>
#include <ctime>
#include <iomanip>
#include <sstream>

namespace minibank {

// Constructor
BankSystem::BankSystem() {}

// Copy constructor
BankSystem::BankSystem(const BankSystem& other)
    : customers_(other.customers_) {}

// Copy assignment operator
BankSystem& BankSystem::operator=(const BankSystem& other) {
    if (this != &other) {
        customers_ = other.customers_;
    }
    return *this;
}

// Destructor
BankSystem::~BankSystem() {
}

// Register new customer
void BankSystem::registerCustomer(const std::string& name) {
    if (customers_.find(name) != customers_.end()) {
        std::cout << "Customer already exists.\n";
        return;
    }

    BankAccount savings;
    savings.accountId = AccountUtils::generateAccountNumber(AccountType::Savings);
    savings.type = AccountType::Savings;
    savings.balance = 0.0;

    BankAccount checking;
    checking.accountId = AccountUtils::generateAccountNumber(AccountType::Checking);
    checking.type = AccountType::Checking;
    checking.balance = 0.0;

    Customer customer(name, savings, checking);
    customers_.emplace(name, customer);

    std::cout << "Customer " << name << " successfully registered!\n";
    std::cout << "Two bank accounts have been created:\n";
    std::cout << "- Savings Account: " << savings.accountId << "\n";
    std::cout << "- Checking Account: " << checking.accountId << "\n";
    pause();
}

const Customer* BankSystem::getCustomer(const std::string& name) const {
    auto it = customers_.find(name);
    if (it != customers_.end()) {
        return &(it->second);
    }
    return nullptr;
}

std::map<std::string, Customer>& BankSystem::getAllCustomers() {
    return customers_;
}

// Access and operate on a bank account
void BankSystem::accessAccount(const std::string& customerName, AccountType type) {
    clearScreen();
    auto it = customers_.find(customerName);
    if (it == customers_.end()) {
        std::cout << "Customer not found.\n";
        return;
    }

    Customer& customer = it->second;
    BankAccount* account = nullptr;

    if (type == AccountType::Savings) {
        account = &customer.getSavingsAccount();
    } else if (type == AccountType::Checking) {
        account = &customer.getCheckingAccount();
    }

    if (!account) {
        std::cout << "Account type not found.\n";
        return;
    }

    std::cout << "===== Bank Account: " << account->accountId << " =====" << std::endl;
    std::cout << "Account holder: " << customerName << std::endl;
    std::cout << "Balance: ¥" << account->balance << std::endl;

    std::cout << "\n1. Deposit" << std::endl;
    std::cout << "2. Withdraw" << std::endl;
    std::cout << "3. Transfer" << std::endl;
    std::cout << "4. View transaction history" << std::endl;
    std::cout << "5. Generate bank account report" << std::endl;
    if (account->type == AccountType::Savings) {
        std::cout << "6. [DEV] Add ¥1000 to savings\n";
        std::cout << "7. Return to customer page\n";
    } else {
        std::cout << "6. Return to customer page\n";
    }

    std::cout << "Please choose: ";
    int choice;
    std::cin >> choice;
    std::cin.ignore();

    if (account->type == AccountType::Savings) {
        switch (choice) {
            case 1:
                std::cout << "Deposit is only allowed to checking accounts.\n";
                pause();
                break;
            case 2:
                std::cout << "Withdraw is only allowed from checking accounts.\n";
                pause();
                break;
            case 3:
                transferFromAccount(*account, customerName);
                break;
            case 4:
                printAccountTransactions(*account);
                break;
            case 5:
                generateAccountReport(*account, customerName);
                break;
            case 6:
                addDebugSavings(customerName);
                break;
            case 7:
                std::cout << "Returning to customer menu...\n";
                break;
            default:
                std::cout << "Invalid choice.\n";
                break;
        }
    } else {
        switch (choice) {
            case 1:
                depositToAccount(*account, customerName);
                break;
            case 2:
                withdrawFromAccount(*account, customerName);
                break;
            case 3:
                transferFromAccount(*account, customerName);
                break;
            case 4:
                printAccountTransactions(*account);
                break;
            case 5:
                generateAccountReport(*account, customerName);
                break;
            case 6:
                std::cout << "Returning to customer menu...\n";
                break;
            default:
                std::cout << "Invalid choice.\n";
                break;
        }
    }

    pause();
}

// Deposit funds
void BankSystem::depositToAccount(BankAccount& account, const std::string& customerName) {
    clearScreen();
    std::cout << "===== Deposit to Account: " << account.accountId << " =====" << std::endl;
    std::cout << "Account holder: " << customerName << std::endl;
    std::cout << "Current Balance: ¥" << account.balance << std::endl;

    double amount;
    std::cout << "Enter amount to deposit: ";
    std::cin >> amount;
    std::cin.ignore();

    if (amount <= 0) {
        std::cout << "Invalid amount. Deposit must be positive.\n";
        return;
    }

    std::string note;
    std::cout << "Add note (optional): ";
    std::getline(std::cin, note);

    account.balance += amount;
    std::string txId = "T" + std::to_string(account.transactions.size() + 1);
    Transaction tx(txId, TransactionType::Deposit, amount, note);
    account.transactions.push_back(tx);
    std::cout << "Deposit successful!\n";
    std::cout << "New balance: ¥" << account.balance << std::endl;
    std::cout << "Transaction ID: " << txId << " has been recorded.\n";

    pause();
}

// Withdraw funds
void BankSystem::withdrawFromAccount(BankAccount& account, const std::string& customerName) {
    clearScreen();
    std::cout << "===== Withdraw from Account: " << account.accountId << " =====" << std::endl;
    std::cout << "Account holder: " << customerName << std::endl;
    std::cout << "Current Balance: ¥" << account.balance << std::endl;

    double amount;
    std::cout << "Enter amount to withdraw: ";
    std::cin >> amount;
    std::cin.ignore();

    if (amount <= 0) {
        std::cout << "Invalid amount. Withdrawal must be positive.\n";
        return;
    }

    if (amount > account.balance) {
        std::cout << "Insufficient funds.\n";
        return;
    }

    std::string note;
    std::cout << "Add note (optional): ";
    std::getline(std::cin, note);

    account.balance -= amount;
    std::string txId = "T" + std::to_string(account.transactions.size() + 1);
    Transaction tx(txId, TransactionType::Withdraw, amount, note);
    account.transactions.push_back(tx);
    std::cout << "Withdrawal successful!\n";
    std::cout << "New balance: ¥" << account.balance << std::endl;
    std::cout << "Transaction ID: " << txId << " has been recorded.\n";

    pause();
}

// Transfer funds
void BankSystem::transferFromAccount(BankAccount& fromAccount, const std::string& customerName) {
    clearScreen();
    std::cout << "===== Transfer from Account: " << fromAccount.accountId << " =====" << std::endl;
    std::cout << "Account holder: " << customerName << std::endl;
    std::cout << "Current Balance: ¥" << fromAccount.balance << std::endl;

    std::string destAccountId;
    std::cout << "Enter destination account ID: ";
    std::getline(std::cin, destAccountId);

    BankAccount* toAccount = nullptr;
    std::string targetCustomer;

    for (auto& pair : customers_) {
        Customer& c = pair.second;
        if (c.getCheckingAccount().accountId == destAccountId) {
            toAccount = &c.getCheckingAccount();
            targetCustomer = c.getName();
            break;
        }
        if (c.getSavingsAccount().accountId == destAccountId) {
            toAccount = &c.getSavingsAccount();
            targetCustomer = c.getName();
            break;
        }
    }

    if (!toAccount) {
        std::cout << "Destination account not found.\n";
        return;
    }

    if (fromAccount.type == AccountType::Savings) {
        if (targetCustomer != customerName || toAccount->type != AccountType::Checking) {
            std::cout << "Savings account can only transfer to your own checking account.\n";
            return;
        }
    }

    double amount;
    std::cout << "Enter amount to transfer: ";
    std::cin >> amount;
    std::cin.ignore();

    if (amount <= 0) {
        std::cout << "Invalid amount. Transfer must be positive.\n";
        return;
    }
    if (amount > fromAccount.balance) {
        std::cout << "Insufficient funds. Transfer failed.\n";
        return;
    }

    std::string note;
    std::cout << "Add note (optional): ";
    std::getline(std::cin, note);

    fromAccount.balance -= amount;
    std::string txId = "T" + std::to_string(fromAccount.transactions.size() + 1);
    Transaction tx(txId, TransactionType::Transfer, amount, note, toAccount->accountId);
    fromAccount.transactions.push_back(tx);
    toAccount->balance += amount;
    std::string txId2 = "T" + std::to_string(toAccount->transactions.size() + 1);
    Transaction tx2(txId2, TransactionType::Deposit, amount, note, fromAccount.accountId);
    toAccount->transactions.push_back(tx2);
    std::cout << "Transfer successful!\n";
    std::cout << "New balance: ¥" << fromAccount.balance << std::endl;
    std::cout << "Transaction ID: " << txId << " has been recorded.\n";

    pause();
}

// Print account transactions
void BankSystem::printAccountTransactions(const BankAccount& account) const {
    clearScreen();
    std::cout << "===== Transaction History for Account: " << account.accountId << " =====\n";
    if (account.transactions.empty()) {
        std::cout << "No transactions found.\n";
    } else {
        for (const auto& tx : account.transactions) {
            std::cout << tx.toString() << std::endl;
        }
    }
    pause();
}

// Generate account report
void BankSystem::generateAccountReport(const BankAccount& account, const std::string& customerName) const {
    clearScreen();
    time_t now = time(nullptr);
    tm* localTime = localtime(&now);
    std::ostringstream filename;
    filename << "../data/transactions/transactions_" << account.accountId << "_"
             << (1900 + localTime->tm_year) << "-"
             << std::setw(2) << std::setfill('0') << (localTime->tm_mon + 1) << "-"
             << std::setw(2) << std::setfill('0') << localTime->tm_mday
             << ".txt";

    std::ofstream outFile(filename.str());
    if (!outFile) {
        std::cerr << "Failed to open file for writing.\n";
        return;
    }

    outFile << "Account Report for: " << customerName << "\n";
    outFile << "Account ID: " << account.accountId << "\n";
    outFile << "Balance: ¥" << account.balance << "\n";
    outFile << "Transaction History:\n\n";

    for (const auto& tx : account.transactions) {
        outFile << tx.toString() << "\n";
    }

    outFile.close();
    std::cout << "Report successfully generated as \"" << filename.str() << "\"\n";
    pause();
}

// Print customer transactions
void minibank::BankSystem::printCustomerTransactions(const std::string& customerName) const {
    clearScreen();
    const Customer* customer = getCustomer(customerName);
    if (!customer) {
        std::cout << "Customer not found.\n";
        return;
    }

    const auto& savings = customer->getSavingsAccount().transactions;
    const auto& checking = customer->getCheckingAccount().transactions;

    std::cout << "===== All Transactions for Customer: " << customerName << " =====\n";

    if (savings.empty() && checking.empty()) {
        std::cout << "No transactions found.\n";
    } else {
        std::cout << "\n-- Savings Account Transactions --\n";
        for (const auto& tx : savings) {
            std::cout << tx.toString() << std::endl;
        }

        std::cout << "\n-- Checking Account Transactions --\n";
        for (const auto& tx : checking) {
            std::cout << tx.toString() << std::endl;
        }
    }

    pause();
}

// Generate customer report
void BankSystem::generateCustomerReport(const std::string& customerName) const {
    clearScreen();
    const Customer* customer = getCustomer(customerName);
    if (!customer) {
        std::cout << "Customer not found.\n";
        return;
    }

    time_t now = time(nullptr);
    tm* localTime = localtime(&now);
    std::ostringstream filename;
    filename << "../data/transactions/transactions_" << customerName << "_"
             << (1900 + localTime->tm_year) << "-"
             << std::setw(2) << std::setfill('0') << (localTime->tm_mon + 1) << "-"
             << std::setw(2) << std::setfill('0') << localTime->tm_mday
             << ".txt";

    std::ofstream outFile(filename.str());
    if (!outFile) {
        std::cerr << "Failed to open file for writing.\n";
        return;
    }

    outFile << "Customer Report for: " << customerName << "\n";
    outFile << "Savings Account: " << customer->getSavingsAccount().accountId << "\n";
    outFile << "Checking Account: " << customer->getCheckingAccount().accountId << "\n";
    outFile << "---------------------------------------------\n";

    outFile << "\n-- Savings Account Transactions --\n";
    for (const auto& tx : customer->getSavingsAccount().transactions) {
        outFile << tx.toString() << "\n";
    }

    outFile << "\n-- Checking Account Transactions --\n";
    for (const auto& tx : customer->getCheckingAccount().transactions) {
        outFile << tx.toString() << "\n";
    }

    outFile.close();
    std::cout << "Report successfully generated as \"" << filename.str() << "\"\n";
    pause();
}

// Developer tool: add ¥1000 to savings
void BankSystem::addDebugSavings(const std::string& customerName) {
    auto it = customers_.find(customerName);
    if (it == customers_.end()) {
        std::cout << "Customer not found.\n";
        return;
    }

    Customer& customer = it->second;
    BankAccount& savings = customer.getSavingsAccount();
    savings.balance += 1000.0;

    std::cout << "[DEV] ¥1000 added to Savings Account: " << savings.accountId << "\n";
    std::cout << "New Balance: ¥" << savings.balance << "\n";

    pause();
}


// Display all bank accounts
void minibank::BankSystem::displayAllAccounts() const {
    clearScreen();
    std::cout << "===== All Bank Accounts =====\n";

    int accountCount = 0;
    double totalBalance = 0.0;

    std::cout << "\nSAVINGS ACCOUNTS:\n";
    for (const auto& [name, customer] : customers_) {
        const auto& savings = customer.getSavingsAccount();
        std::cout << "ID: " << savings.accountId
                  << "  |  Owner: " << name
                  << "  |  Balance: ¥" << savings.balance << "\n";
        ++accountCount;
        totalBalance += savings.balance;
    }

    std::cout << "\nCHECKING ACCOUNTS:\n";
    for (const auto& [name, customer] : customers_) {
        const auto& checking = customer.getCheckingAccount();
        std::cout << "ID: " << checking.accountId
                  << "  |  Owner: " << name
                  << "  |  Balance: ¥" << checking.balance << "\n";
        ++accountCount;
        totalBalance += checking.balance;
    }

    std::cout << "\nTotal Accounts: " << accountCount;
    std::cout << "\nTotal Customers: " << customers_.size();
    std::cout << "\nTotal Balance: ¥" << totalBalance << "\n";

    pause();
}

// Generate global transaction report
void BankSystem::generateGlobalTransactionReport() const {
    clearScreen();
    time_t now = time(nullptr);
    tm* localTime = localtime(&now);
    std::ostringstream filename;
    filename << "../data/transactions/transactions_"
             << (1900 + localTime->tm_year) << "-"
             << std::setw(2) << std::setfill('0') << (localTime->tm_mon + 1) << "-"
             << std::setw(2) << std::setfill('0') << localTime->tm_mday
             << ".txt";

    std::ofstream outFile(filename.str());
    if (!outFile) {
        std::cerr << "Failed to open file for writing.\n";
        return;
    }

    outFile << "Global Transaction Report\n";
    outFile << "==========================\n\n";

    for (const auto& [name, customer] : customers_) {
        const auto& savings = customer.getSavingsAccount();
        const auto& checking = customer.getCheckingAccount();

        for (const auto& tx : savings.transactions) {
            outFile << "[SAVINGS] " << name << " | " << savings.accountId << " | " << tx.toString() << "\n";
        }

        for (const auto& tx : checking.transactions) {
            outFile << "[CHECKING] " << name << " | " << checking.accountId << " | " << tx.toString() << "\n";
        }
    }

    outFile.close();
    std::cout << "Global report successfully generated as \"" << filename.str() << "\"\n";
    pause();
}

} 

